

#include "Text.h"
#include "Mesh.h"
#include "Input.h"
#include "ResMgr.h"
#include "Shader.h"
#include "Camera.h"
#include "Display.h"

#define WIDTH 1280
#define HEIGHT 720

#define SIZE(arrays) sizeof(arrays) / sizeof(arrays[0])

#ifndef DEBUG
#pragma comment(linker, "/subsystem:\"windows\"  /entry:\"mainCRTStartup\"")
#endif

class mesh {
public:
	mesh(const GLfloat* vertexs, GLuint vercount, const GLuint* indexes, GLuint indexcount) {
		isIndex = true;
		indexCount = indexcount;
		glGenVertexArrays(1, &vao);
		glGenBuffers(1, &vbo);
		glGenBuffers(1, &ebo);
		
		glBindVertexArray(vao);
		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, vercount * sizeof(vertexs), vertexs, GL_STATIC_DRAW);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexcount * sizeof(indexes), indexes, GL_STATIC_DRAW);

		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);

		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindVertexArray(0);
	}

	mesh(const GLfloat* vertexs, GLuint vercount) {
		isIndex = false;
		indexCount = vercount;
		glGenVertexArrays(1, &vao);
		glGenBuffers(1, &vbo);

		glBindVertexArray(vao);
		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, vercount * sizeof(vertexs), vertexs, GL_STATIC_DRAW);

		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);

		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
		glEnableVertexAttribArray(1);

		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
		glEnableVertexAttribArray(2);

		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindVertexArray(0);
	}

	~mesh() {
		glDeleteVertexArrays(1, &vao);
		glDeleteBuffers(1, &vbo);
		glDeleteBuffers(1, &ebo);
	}

	GLvoid draw() {
		if (isIndex) {
			glBindVertexArray(vao);
			glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
		}
		else {
			glBindVertexArray(vao); 
			glDrawArrays(GL_TRIANGLES, 0, indexCount);
		}
	}
private:
	GLboolean isIndex = false;
	GLuint vao, vbo, ebo, indexCount;
};

float cube[] = {
	// positions          // normals           // texture coords
	-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,
	 0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,
	 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,

	 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
	-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,
	-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,

	-0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,
	 0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 0.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,
	-0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 1.0f,
	-0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,

	-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
	-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
	-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
	-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
	-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
	-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

	 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
	 0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
	 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
	 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
	 0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
	 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

	-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
	 0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,
	 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
	 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
	-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,
	-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,

	-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,
	 0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
	-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,
	-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f
};

float panel[] = {
	-0.5f, -0.5f,  0.0f,  0.0f,  0.0f, 1.0f,  0.0f, 0.0f,
	-0.5f,  0.5f,  0.0f,  0.0f,  0.0f, 1.0f,  0.0f, 100,
	 0.5f, -0.5f,  0.0f,  0.0f,  0.0f, 1.0f,  100, 0.0f,

	-0.5f,  0.5f,  0.0f,  0.0f,  0.0f, 1.0f,  0.0f, 100,
	 0.5f,  0.5f,  0.0f,  0.0f,  0.0f, 1.0f,  100, 100,
	 0.5f, -0.5f,  0.0f,  0.0f,  0.0f, 1.0f,  100, 0.0f,
};

int main(int argc, char** argv) {
	ResMgr::GetInst()->Init(argv[0]);
	Display display(WIDTH, HEIGHT, "Demo");

	Shader shader,lightShader,fontShader, shaderPanel;
	ResMgr::GetInst()->LoadShader("/res/shader/font.vert", "/res/shader/font.frag", fontShader);
	ResMgr::GetInst()->LoadShader("/res/shader/shader.vert", "/res/shader/shader.frag", shader);
	ResMgr::GetInst()->LoadShader("/res/shader/shader.vert", "/res/shader/shader.frag", shaderPanel);
	ResMgr::GetInst()->LoadShader("/res/shader/light.vert", "/res/shader/light.frag", lightShader);

	Text text(ResMgr::GetInst()->rootPath + "/res/fonts/consola.ttf", fontShader);

	bool firstMouse = true;
	float lastX, lastY;
	lastX = lastY = 0.0f;

	mesh cubeMesh(cube, sizeof(cube) / sizeof(cube[0]));
	mesh panelMesh(panel, sizeof(panel) / sizeof(panel[0]));

	GLuint diffuseMap = ResMgr::GetInst()->LoadTexture("/res/img/container2.png");
	GLuint specularMap = ResMgr::GetInst()->LoadTexture("/res/img/container2_specular.png");

	GLuint panelMap = ResMgr::GetInst()->LoadTexture("/res/img/bricks.jpg");
	GLuint panelMap2 = ResMgr::GetInst()->LoadTexture("/res/img/bricks.jpg");

	glm::mat4 model;
	glm::vec3 cubePos(0, 0, 0);
	glm::vec3 cameraPos(-3, 0, 0);
	glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
	glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WIDTH / (float)HEIGHT, 0.001f, 1000.0f);
	glm::mat4 fontProjection = glm::ortho(0.0f, static_cast<GLfloat>(WIDTH), 0.0f, static_cast<GLfloat>(HEIGHT));

	Camera camera(cameraPos);

	shader.Use();
	model = glm::translate(model, cubePos);
	shader.setMat4("model", model);
	shader.setMat4("projection", projection);

	shader.setInt("material.diffuse", 0);
	shader.setInt("material.specular", 1);

	shaderPanel.Use();
	shaderPanel.setMat4("projection", projection);
	shaderPanel.setInt("material.diffuse", 0);

	lightShader.Use();
	model = glm::translate(model, lightPos);
	model = glm::scale(model, glm::vec3(0.2f));
	lightShader.setMat4("model", model);
	lightShader.setMat4("projection", projection);
	lightShader.setMat4("view", camera.GetViewMatrix());

	shaderPanel.Use();
	shaderPanel.setInt("material.diffuse", 0);
	shaderPanel.setInt("material.specular", 1);
	

	fontShader.Use();
	fontShader.setMat4("projection",fontProjection);

	Input::SetMouseCursor(GLFW_CURSOR_DISABLED);

	while (!display.IsEixt()) {
		display.Clean();

		if (Input::GetKeyDown(GLFW_KEY_ESCAPE))
			break;
		
		if (Input::GetKey(GLFW_KEY_W))
			camera.ProcessKeyboard(FORWARD,display.deltaTime);
		if (Input::GetKey(GLFW_KEY_S))
			camera.ProcessKeyboard(BACKWARD, display.deltaTime);
		if (Input::GetKey(GLFW_KEY_A))
			camera.ProcessKeyboard(LEFT, display.deltaTime);
		if (Input::GetKey(GLFW_KEY_D))
			camera.ProcessKeyboard(RIGHT, display.deltaTime);

		float xoffset = Input::mousePosition.x - lastX;
		float yoffset = lastY - Input::mousePosition.y;
		lastX = Input::mousePosition.x;
		lastY = Input::mousePosition.y;
		camera.ProcessMouseMovement(xoffset, yoffset);
		/*if (Input::GetMouseButton(1)) {
			if (firstMouse) {
				Input::SetMouseCursor(GLFW_CURSOR_DISABLED);
				lastX = Input::mousePosition.x;
				lastY = Input::mousePosition.y;
				firstMouse = false;
			}
			float xoffset = Input::mousePosition.x - lastX;
			float yoffset = lastY - Input::mousePosition.y;
			lastX = Input::mousePosition.x;
			lastY = Input::mousePosition.y;
			camera.ProcessMouseMovement(xoffset, yoffset);
		}
		else {
			if (!firstMouse) {
				firstMouse = true;
				Input::SetMouseCursor(GLFW_CURSOR_NORMAL);
			}
		}*/


		glm::mat4 view = camera.GetViewMatrix();

		shader.Use();

		shader.setMat4("view", view);
		shader.setVec3("viewPos", camera.Position);
		shader.setVec3("light.position", lightPos);

		glm::vec3 lightColor = glm::vec3(1);
		lightColor.x = (float)sin(glfwGetTime() * 2.0f);
		lightColor.y = (float)sin(glfwGetTime() * 0.7f);
		lightColor.z = (float)sin(glfwGetTime() * 1.3f);
		glm::vec3 diffuseColor = lightColor   * glm::vec3(0.5f);
		glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);


		shader.setVec3("light.ambient", diffuseColor);
		shader.setVec3("light.diffuse", ambientColor);
		shader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

		// material properties
		shader.setFloat("material.shininess", 64.0f);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, diffuseMap);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, specularMap);
		
		cubeMesh.draw();



		shaderPanel.Use();
		shaderPanel.setMat4("view", view);
		shaderPanel.setVec3("viewPos", camera.Position);
		shaderPanel.setVec3("light.position", lightPos);

		shaderPanel.setVec3("light.ambient", diffuseColor);
		shaderPanel.setVec3("light.diffuse", ambientColor);
		shaderPanel.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

		shaderPanel.setFloat("material.shininess", 6.0f);

		glm::mat4 model2;
		model2 = glm::translate(model2, glm::vec3(0, -0.5f, 0));
		model2 = glm::rotate(model2, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
		model2 = glm::scale(model2, glm::vec3(100, 100, 1));
		shaderPanel.setMat4("model", model2);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, panelMap);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, panelMap2);

		panelMesh.draw();
		


		lightShader.Use();
		lightShader.setVec3("lightColor", lightColor);
		lightShader.setMat4("view", view);

		cubeMesh.draw();


		model = glm::mat4();
		model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));

		// shader.setMat4("model", model);


		// text.Draw("This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
		// text.Draw("(C) LearnOpenGL.com", 1020.0f, 680.0f, 0.5f, glm::vec3(0.3, 0.7f, 0.9f));

		display.SwapBuffers();
	}

	return 0;
}